package com.lianzai.common.utils.okhttp;

import com.fasterxml.jackson.core.type.TypeReference;
import com.google.common.base.Strings;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.Optional;

import com.lianzai.common.utils.BaseResult;
import com.lianzai.common.utils.ResultCode;
import com.lianzai.common.utils.jackson.Json;
import okhttp3.FormBody;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Request.Builder;
import okhttp3.RequestBody;
import okhttp3.Response;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;

@Configuration
public class LzOkHttpClient {

  private static final Logger logger = LoggerFactory.getLogger(LzOkHttpClient.class.getSimpleName());
  public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");

  @Autowired
  private OkHttpClient okHttpClient;

  @Autowired
  private Json hnJson;


  public <T> BaseResult<T> get(LzOkHttpBuilder hnGetBuilder) {
    checkBasicParam(hnGetBuilder);
    Builder builder = createRequestBuilder(
        createGetUrl(hnGetBuilder.getUrl(), hnGetBuilder.getParams()), hnGetBuilder.getHeaders());
    return returnBaseResult(builder.get().build(), hnGetBuilder.getTypeReference());
  }


  public <T> BaseResult<T> post(LzOkHttpBuilder hnPostBuilder) {
    checkBasicParam(hnPostBuilder);
    Builder builder = createRequestBuilder(hnPostBuilder.getUrl(), hnPostBuilder.getHeaders());

    FormBody.Builder formBuilder = new FormBody.Builder();
    Optional.ofNullable(hnPostBuilder.getParams()).ifPresent(c -> c.forEach(
        (key, value) -> {
          if (value != null) {
            formBuilder.add(key, value);
          }else{
            logger.warn("key:{} is null",key);
          }
        }
    ));

    return returnBaseResult(builder.post(formBuilder.build()).build(),
        hnPostBuilder.getTypeReference()
    );
  }


  public <T> BaseResult<T> postJson(LzOkHttpBuilder hnPostBuilder) {
    checkBasicParam(hnPostBuilder);
    Builder builder = createRequestBuilder(hnPostBuilder.getUrl(), hnPostBuilder.getHeaders());
    checkBody(hnPostBuilder.getBody());
    RequestBody body = RequestBody.create(JSON, hnPostBuilder.getBody());

    return returnBaseResult(builder.post(body)
        .build(), hnPostBuilder.getTypeReference());
  }

  @SuppressWarnings("unchecked")
  private <T> BaseResult<T> returnBaseResult(Request request, TypeReference typeReference) {
    try {
      Response response = this.okHttpClient.newCall(request).execute();
      if (response.isSuccessful()) {
        String result = response.body().string();
        checkResult(result);
        Type type = typeReference.getType();
        if (isPrimitiveType(type)) {
          return new BaseResult(result);
        }

        T t = hnJson.str2obj(result, typeReference);
        if (t instanceof BaseResult) {
          return (BaseResult<T>) t;
        } else {
          return BaseResult.success(t);
        }
      } else {
        logger.error("request url: {} http status code:{}", request.url(), response.code());
        String result = Optional.ofNullable(response.body())
            .map(body -> {
              try {
                return Strings.emptyToNull(body.string());
              } catch (IOException e) {
                logger.error("获取body失败:", e);
                return null;
              }
            })
            .orElse(ResultCode.HTTP_ERROR.getMsg());
        return BaseResult.fail(ResultCode.HTTP_ERROR.getCode(), result);
      }
    } catch (Exception e) {
      throw new OkHttpException(e);
    }
  }


  private boolean isPrimitiveType(Type type) {
    if (type instanceof Class) {
      Class clazz = (Class) type;
      if (ClassUtils.isPrimitiveOrWrapper(clazz) || ClassUtils
          .isAssignable(clazz, String.class)) {
        return true;
      }
    }
    return false;
  }

  private void checkBody(String body) {
    if (StringUtils.isBlank(body)) {
      throw new RuntimeException("body 不能为空!");
    }
  }


  private void checkResult(String result) {
    if (StringUtils.isBlank(result)) {
      throw new RuntimeException("返回值为空!");
    }
  }

  private Builder createRequestBuilder(String url, Map<String, String> headers) {
    Builder builder = new Builder();
    builder.url(url);
    Optional.ofNullable(headers).ifPresent(c -> c.forEach(builder::addHeader));
    return builder;
  }

  private void checkBasicParam(LzOkHttpBuilder hnOkHttpBuilder) {
    if (StringUtils.isBlank(hnOkHttpBuilder.getUrl())) {
      throw new RuntimeException("不能为空!");
    }

    if (hnOkHttpBuilder.getTypeReference() == null) {
      throw new RuntimeException("typeReference!");
    }
  }

  private String createGetUrl(String url, Map<String, String> params) {
    if (CollectionUtils.isEmpty(params)) {
      return url;
    }
    StringBuilder sb = new StringBuilder();
    params.forEach((k, v) -> {
      if (sb.length() == 0) {
        sb.append("?");
      } else if (sb.length() > 0) {
        sb.append("&");
      }
      sb.append(k);
      sb.append("=").append(v);
    });
    return url + sb.toString();
  }


}
